#include "Graphics/OpenGL/Win32/GLWin32RenderWindow.h"
#include "Graphics/IViewport.h"
#include "OS/WindowEventUtils.h"

namespace tyro
{
	namespace Graphics
	{

		GLWin32RenderWindow::GLWin32RenderWindow()
			: context(0)
		{
			isFullscreen = false;
			isExternal = false;
			isHidden = false;
			deviceName = NULL;
			renderContext = 0;
			windowHandle = 0;
			windowedStyle = 0;
			fullscreenStyle = 0;
		}

		GLWin32RenderWindow::~GLWin32RenderWindow()
		{
			Release();
		}

		void GLWin32RenderWindow::Create( const char* const title, unsigned int width, unsigned int height, bool IsFullScreen )
		{
			isFullscreen = IsFullScreen;

			HINSTANCE winInstance = GetModuleHandle( NULL );

			DWORD window_style;
			DWORD window_style_ex;

			RECT windowRect = { 0, 0, width, height };

			this->width = width;
			this->height = height;


			// Register the window class
			WNDCLASS windowClass;												// Windows Class Structure
			windowClass.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
			windowClass.lpfnWndProc		= OS::WindowEventUtils::_WndProc;		// WndProc Handles Messages
			windowClass.cbClsExtra		= 0;									// No Extra Window Data
			windowClass.cbWndExtra		= 0;									// No Extra Window Data
			windowClass.hInstance		= winInstance;							// Set The Instance
			windowClass.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			// Load The Default Icon
			windowClass.hCursor			= LoadCursor( NULL, IDC_ARROW );		// Load The Arrow Pointer
			windowClass.hbrBackground	= NULL;									// No Background Required For GL
			windowClass.lpszMenuName	= NULL;									// We Don't Want A Menu
			windowClass.lpszClassName	= "TyroWindow";							// Set The Class Name


			RegisterClass( &windowClass );	// Attempt To Register The Window Class


			window_style_ex = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;		// Window Extended Style
			window_style = WS_OVERLAPPEDWINDOW;							// Windows Style


			BOOL adjusted = AdjustWindowRectEx(&windowRect, window_style, FALSE, window_style_ex);
			assert(adjusted);


			// Create the window
			if(!(windowHandle = CreateWindowEx(	window_style_ex,		// Extended Style For The Window
									"TyroWindow",						// Class Name
									title,								// Window Title
									window_style |						// Defined Window Style
									WS_CLIPSIBLINGS |					// Required Window Style
									WS_CLIPCHILDREN,					// Required Window Style
									0, 0,								// Window Position
									windowRect.right-windowRect.left,	// Calculate Window Width
									windowRect.bottom-windowRect.top,	// Calculate Window Height
									NULL,								// No Parent Window
									NULL,								// No Menu
									winInstance,						// Instance
									this)))								// Dont Pass Anything To WM_CREATE
			{
			}

			DWORD errorX = GetLastError();

			deviceContext = GetDC(windowHandle);	// Did We Get A Device Context?
			

			colourDepth = 16;


			setPixelFormat(deviceContext, colourDepth, 0, false);

			renderContext = wglCreateContext(deviceContext);
			wglMakeCurrent(deviceContext, renderContext);

			context = new GLWin32Context(deviceContext, renderContext);

			SetHidden(false);
		}

		void GLWin32RenderWindow::SetFullscreen( bool IsFullscreen, unsigned int width, unsigned int height )
		{
			//TODO set window full screen
		}

		void GLWin32RenderWindow::SetHidden( bool isHidden )
		{
			this->isHidden = isHidden;

			if(!isExternal)
			{
				if (isHidden)
				{
					ShowWindow(windowHandle, SW_HIDE);
				}
				else
				{
					ShowWindow(windowHandle, SW_SHOWNORMAL);
				}
			}

		}

		void GLWin32RenderWindow::swapBuffers()
		{
			SwapBuffers(deviceContext);
		}

		void GLWin32RenderWindow::Release( void )
		{
			if (!windowHandle)
			{
				return;
			}

			// Delete the active context
			delete context;

			ReleaseDC(windowHandle, deviceContext);

			windowHandle = 0;
			deviceContext = 0;
		}

		bool GLWin32RenderWindow::IsFullscreen( void ) const
		{
			return isFullscreen;
		}

		void GLWin32RenderWindow::Resize( UINT_32 width, UINT_32 height )
		{

		}

		void GLWin32RenderWindow:: GetCustomAttribute(const String& attribute,  void* dest)
		{
			if (attribute == "GLCONTEXT")
			{
				*static_cast<GLContext**>(dest) = context;
				return;
			} 
			else if (attribute == "WINDOW")
			{
				HWND *Hwnd = (HWND*)dest;
				*Hwnd = GetWindowHandle();
				return;
			}


		}

		void GLWin32RenderWindow::WindowMovedOrResized()
		{
			if (!windowHandle)
			{
				return;
			}

			UpdateWindowRect();
		}

		void GLWin32RenderWindow::UpdateWindowRect()
		{
			RECT rect;
			BOOL result;

			// Update top left parameters
			result = GetWindowRect(windowHandle, &rect);
			if (result == FALSE)
			{
				top = 0;
				left = 0;
				width = 0;
				height = 0;
				return;
			}

			top = rect.top;
			left = rect.left;

			// width and height represent drawable area only
			result = GetClientRect(windowHandle, &rect);
			if (result == FALSE)
			{
				top = 0;
				left = 0;
				width = 0;
				height = 0;
				return;
			}

			unsigned int newWidth = rect.right - rect.left;
			unsigned int newHeight = rect.bottom - rect.top;

			// Case window resized.
			if (newWidth != width || newHeight != height)
			{
				width  = rect.right - rect.left;
				height = rect.bottom - rect.top;

				ViewportMap::iterator it = viewports.begin();
				while (it != viewports.end())
				{
					// Update each viewport dimensions
					(*it++).second->UpdateDimensions();
				}		
			}
		}

		void GLWin32RenderWindow::setPixelFormat( HDC deviceContext, UINT_32 colourDepth, UINT_32 multisample, bool gamma )
		{

			PIXELFORMATDESCRIPTOR pfd;
			memset(&pfd, 0, sizeof(pfd));
			pfd.nSize = sizeof(pfd);
			pfd.nVersion = 1;
			pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
			pfd.iPixelType = PFD_TYPE_RGBA;
			pfd.cColorBits = (colourDepth > 16)? 24 : colourDepth;
			pfd.cAlphaBits = (colourDepth > 16)? 8 : 0;
			pfd.cDepthBits = 16;
			pfd.cStencilBits = 0;

			UINT_32 format = ChoosePixelFormat(deviceContext, &pfd);

			SetPixelFormat(deviceContext, format, &pfd);

		}

	}
}